<template>
  <el-col class="czr-form-column" :class="{
    span1: filterSpan == 1,
    span2: filterSpan == 2,
    span3: filterSpan == 3,
    span4: filterSpan == 4,
    span5: filterSpan == 5,
  }" :span="span" :offset="offset" ref="ref_czrFormColumn">
    <el-form-item :label="label" :label-width="labelWidth" :class="{
      ['link_' + link + ($attrs.type ? '_' + $attrs.type : '')]: true,
      required: required !== false,
      'no-label': (labelWidth == 0 || labelWidth === '0px') && !isValue(label) && !$slots.label,
      'is-error': !!state.errorMessage
    }" :error="state.errorMessage" :required="required">
      <template #label>
        <slot name="label"/>
      </template>
      <slot name="czr" :handleValidate="handleValidate">
        <template v-if="link === 'input'">
          <InputCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
              @emitEnter="handleEnter"
          >
            <template v-if="$slots.prefix" #prefix>
              <slot name="prefix"/>
            </template>
            <template v-if="$slots.suffix" #suffix>
              <slot name="suffix"/>
            </template>
            <template v-if="$slots.prepend" #prepend>
              <slot name="prepend"/>
            </template>
            <template v-if="$slots.append" #append>
              <slot name="append"/>
            </template>
          </InputCom>
        </template>
        <template v-else-if="link === 'select'">
          <SelectCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
          >
            <slot/>
          </SelectCom>
        </template>
        <template v-else-if="link === 'date'">
          <DateCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
          >
            <template #default="{cell}">
              <slot v-bind="cell"/>
            </template>
          </DateCom>
        </template>
        <template v-else-if="link === 'datetime'">
          <DateTimeCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
          >
            <template #default="{cell}">
              <slot v-bind="cell"/>
            </template>
          </DateTimeCom>
        </template>
        <template v-else-if="link === 'time'">
          <TimeCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
          >
          </TimeCom>
        </template>
        <template v-else-if="link === 'cascader'">
          <CascaderCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
          >
            <template v-if="$slots.default" #default="{node, data}">
              <slot v-bind="{node, data}"/>
            </template>
          </CascaderCom>
        </template>
        <template v-else-if="link === 'switch'">
          <SwitchCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
          >
          </SwitchCom>
        </template>
        <template v-else-if="link === 'radio'">
          <RadioCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
          >
          </RadioCom>
        </template>
        <template v-else-if="link === 'checkbox'">
          <CheckboxCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
          >
          </CheckboxCom>
        </template>
        <template v-else-if="link === 'number'">
          <NumberCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
              @emitEnter="handleEnter"
          >
            <template v-if="$slots.prefix" #prefix>
              <slot name="prefix"/>
            </template>
            <template v-if="$slots.suffix" #suffix>
              <slot name="suffix"/>
            </template>
            <template v-if="$slots.prepend" #prepend>
              <slot name="prepend"/>
            </template>
            <template v-if="$slots.append" #append>
              <slot name="append"/>
            </template>
          </NumberCom>
        </template>
        <template v-else-if="link === 'input-number'">
          <InputNumberCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
              @emitEnter="handleEnter"
          >
          </InputNumberCom>
        </template>
        <template v-else-if="link === 'tree-select'">
          <TreeSelectCom
              v-bind="$attrs"
              :label="labelCpt"
              :param="param"
              @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
          >
            <template v-if="$slots.default" #default="prop">
              <slot name="default" v-bind="prop"/>
            </template>
          </TreeSelectCom>
        </template>
        <template v-else-if="link === 'upload'">
          <UploadCom
            v-bind="$attrs"
            :label="labelCpt"
            :param="param"
            @emitParam="val => {
                $emit('update:param', val), handleValidate(val)
              }"
          />
        </template>
      </slot>
      <div v-if="unit" class="unit">{{unit}}</div>
      <slot name="unit"/>
    </el-form-item>
  </el-col>
</template>

<script setup lang="ts">
defineOptions({
  name: 'CzrFormColumn',
});
import {
  computed,
  onMounted,
  ref,
  reactive,
  watch,
  getCurrentInstance,
  ComponentInternalInstance,
  inject,
  onBeforeUnmount
} from 'vue'
import InputCom from './czr-form-link/input.vue'
import InputNumberCom from './czr-form-link/input-number.vue'
import SelectCom from './czr-form-link/select.vue'
import DateCom from './czr-form-link/date.vue'
import DateTimeCom from './czr-form-link/datetime.vue'
import TimeCom from './czr-form-link/time.vue'
import CascaderCom from './czr-form-link/cascader.vue'
import SwitchCom from './czr-form-link/switch.vue'
import RadioCom from './czr-form-link/radio.vue'
import CheckboxCom from './czr-form-link/checkbox.vue'
import NumberCom from './czr-form-link/number.vue'
import TreeSelectCom from './czr-form-link/tree-select.vue'
import UploadCom from './czr-form-link/upload.vue'
import { v4 } from "uuid";
const props = defineProps({
  span: {type: Number, default: 6}, // 栅格
  filterSpan: { default: null },  // 列表筛选条件单独5列布局
  offset: {type: Number, default: 0}, // 栅格
  param: {},  // 绑定值
  label: {type: String, default: ''}, // 标题
  required: {default: false}, // 必填项
  labelWidth: {type: String, default: 'auto'},  // 标题宽度
  link: {type: String, default: 'input', validator(val: string) {
      return ['cascader', 'checkbox', 'date', 'datetime', 'input', 'radio', 'select', 'switch', 'dept', 'time', 'upload', 'number', 'input-number', 'tree-select', 'rich', 'area'].includes(val)
    }
  },  // 类型，为了避免与原生type字段重复
  rules: {type: Array, default: () => []},  // 自定义规则
  maxLength: {type: Number, default: null}, // 最大长度
  minLength: {type: Number, default: null}, // 最小长度
  defaultErrorMsg: {default: null}, // 默认校验错误提示
  unit: {default: '', type: String},  // 单位
  otherInfo: {} // 其他信息
})
const attrs = (getCurrentInstance() as ComponentInternalInstance).attrs
const state = reactive({
  errorMessage: null,
  uuid: ''
})
const ref_czrFormColumn: any = ref(null)
const isValue = (val: any) => {
  if (val === null || val === undefined || (typeof val === 'string' && val.trim() === '') || (typeof val === 'object' && val?.length === 0)) {
    return false
  }
  return true
}
const labelCpt = computed(() => {
  return props.label.replace(/[:：]([^:：]*)$/, "")
})
const rulesCpt = computed(() => {
  const r = [...props.rules]
  if (isValue(props.minLength)) {
    r.unshift({
      handle: (val: any) => !isValue(val) || (isValue(val) && String(val).length >= props.minLength),
      message: `内容过短，字数需大于等于${props.minLength}`
    })
  }
  if (isValue(props.maxLength)) {
    r.unshift({
      handle: (val: any) => !isValue(val) || (isValue(val) && String(val).length <= props.maxLength),
      message: `内容过长，字数需小于等于${props.maxLength}`
    })
  } else if (props.link === 'input' && attrs.type === 'textarea' && isValue(attrs.maxlength)) {
    r.unshift({
      handle: (val: any) => !isValue(val) || (isValue(val) && String(val).length <= Number(attrs.maxlength)),
      message: `内容过长，字数需小于等于${attrs.maxlength}`
    })
  }
  const doStr = ['input', 'number', 'input-number', 'rich'].includes(props.link) ? '输入' : '选择'
  if (props.required !== false && !props.rules.some((v: any) => v.type === 'default')) {
    r.unshift({
      handle: (val: any) => isValue(val),
      message: props.defaultErrorMsg ?? `请${doStr}${labelCpt.value}`
    })
  }
  return r
})
const handleValidate = (val: any = undefined, val2: any = undefined) => {
  state.errorMessage = null
  for (let i = 0; i < rulesCpt.value.length; i++) {
    const item: any = rulesCpt.value[i]
    if (!item.handle(val === undefined ? props.param : val)) {
      state.errorMessage = item.message
      break
    }
  }
  return state.errorMessage
}
const handleEnterFunc = inject('handle-enter', () => {})
const handleEnter = () => {
  handleEnterFunc?.()
}
const reset = () => {
  state.errorMessage = null
}
watch(() => state.errorMessage, (n) => {
  const p = ref_czrFormColumn.value?.$el?.getElementsByClassName('el-form-item')?.[0]
  if (n) {
    setTimeout(() => {
      const e = p?.getElementsByClassName('el-form-item__content')?.[0]?.getElementsByClassName('el-form-item__error')?.[0]
      if (e?.clientHeight) {
        p.style.marginBottom = `${e.clientHeight + 4}px`
      }
    }, 200)
  } else {
    p.style.marginBottom = '18px'
  }
})
onMounted(() => {
  state.uuid = v4()
  const formChildrenMap: any = inject('czr-form-children-map', new Map())
  let flag = true
  const deep = (dom) => {
    if (dom.className === 'hidden-columns') {
      flag = false
    }
    if (dom.parentElement) {
      deep(dom.parentElement)
    }
  }
  deep(ref_czrFormColumn.value.$parent.$el)
  if (flag) {
    formChildrenMap.set(state.uuid, ref_czrFormColumn.value.$parent)
  }
})
onBeforeUnmount(() => {
  const formChildrenMap: any = inject('czr-form-children-map', new Map())
  formChildrenMap.delete(state.uuid)
})
defineExpose({
  reset,
  handleValidate,
  handleEnter,
})
</script>

<style scoped lang="scss">
.czr-form-column {
  &.span1 {
    width: 20%;
    max-width: 20%;
    flex: unset;
  }
  &.span2 {
    width: 40%;
    max-width: 40%;
    flex: unset;
  }
  &.span3 {
    width: 60%;
    max-width: 60%;
    flex: unset;
  }
  &.span4 {
    width: 80%;
    max-width: 80%;
    flex: unset;
  }
  &.span5 {
    width: 100%;
    max-width: 100%;
    flex: unset;
  }
  :deep(.el-form-item) {
    &.link_time {
      margin-top: 1px;
    }
    &.no-label {
      .el-form-item__label {
        display: none;
      }
    }
    $textColor: #576275;
    .el-form-item__label {
      line-height: 1;
      text-align: right;
      display: flex;
      align-items: center;
      padding-left: 10px;
      color: $textColor;
      font-weight: normal;
    }
    .el-form-item__content {
      flex-wrap: unset;
      >div:first-child {
        flex: 1;
      }
      .unit {
        margin-left: 6px;
        font-size: 14px;
        font-family: PingFang SC-Regular, PingFang SC;
        font-weight: 400;
        color: #606266;
      }
      .is-disabled {
        .el-select__selected-item:not(.is-transparent),
        .el-input__inner,
        .el-radio__label,
        .el-checkbox__label,
        .el-range-input, .el-range-separator,
        .el-textarea__inner
        {
          color: $textColor;
          -webkit-text-fill-color: $textColor;
        }
      }
    }
  }
}
</style>
